// sktoolslib - common files for SK tools

// Copyright (C) 2012, 2020 - Stefan Kueng

// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software Foundation,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//

#include "stdafx.h"
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <crtdbg.h>
#include "ResourceTextFile.h"

CResourceTextFile::CResourceTextFile()
    : m_pszText(NULL)
    , m_eBomAction(NoBomAction)
    , m_eConvertAction(NoConvertAction)
{
}

CResourceTextFile::CResourceTextFile(const CResourceTextFile &rf)
{
    if (rf.m_bDoNotDeleteBuffer)
    {
        // buffer is allocated externally or has been detached
        m_pszText = rf.m_pszText;
    }
    else
    {
        m_pszText = new wchar_t[rf.m_nBufLen + 2];
        memset(m_pszText, 0, (rf.m_nBufLen + 2) * sizeof(wchar_t));
        wcsncpy_s(m_pszText, rf.m_nBufLen + 2, rf.m_pszText, rf.m_nBufLen);
    }

    m_nBufLen            = rf.m_nBufLen;
    m_nPosition          = 0;
    m_bIsOpen            = rf.m_bIsOpen;
    m_bText              = rf.m_bText;
    m_bDoNotDeleteBuffer = rf.m_bDoNotDeleteBuffer;
    m_eConvertAction     = rf.m_eConvertAction;
    m_eBomAction         = rf.m_eBomAction;
}

CResourceTextFile::~CResourceTextFile()
{
    Close();
}

BOOL CResourceTextFile::Open(HINSTANCE     hInstance,
                             LPCWSTR       lpszResId,
                             LPCWSTR       lpszResType /*= L"TEXT"*/,
                             ConvertAction eConvertAction /*= NoConvertAction*/,
                             BomAction     eBomAction /*= NoBomAction*/)
{
    BOOL rc = FALSE;

    Close();

    _ASSERTE(lpszResId);
    _ASSERTE(lpszResType);

    m_eConvertAction = eConvertAction;
    m_eBomAction     = eBomAction;

    if (lpszResId && lpszResType)
    {
        rc = CResourceFile::Open(hInstance, lpszResId, lpszResType);

        if (rc)
        {
            wchar_t *cp     = (wchar_t *)GetByteBuffer();
            DWORD    dwSize = (DWORD)GetLength();

            rc = SetTextBuffer(cp, dwSize,
                               eConvertAction, eBomAction);

            if (rc)
            {
                m_bText = TRUE;
            }
            else
            {
                Close();
            }
        }
    }

    return rc;
}

void CResourceTextFile::Close()
{
    CResourceFile::Close();

    if (m_pszText && !m_bDoNotDeleteBuffer)
        delete[] m_pszText;
    m_pszText = NULL;
}

wchar_t *CResourceTextFile::DetachTextBuffer()
{
    wchar_t *cp = NULL;

    if (m_bIsOpen && m_bText)
    {
        m_bDoNotDeleteBuffer = TRUE;
        cp                   = m_pszText;
    }

    return cp;
}

wchar_t *CResourceTextFile::DuplicateTextBuffer()
{
    wchar_t *dup = NULL;

    if (IsOpen() && m_bText)
        dup = _wcsdup(m_pszText);

    return dup;
}

BOOL CResourceTextFile::SetTextBuffer(wchar_t *     inbuf,
                                      DWORD         len,
                                      ConvertAction eConvertAction /*= NoConvertAction*/,
                                      BomAction     eBomAction /*= NoBomAction*/)
{
    BOOL rc = FALSE;

    _ASSERTE(inbuf);
    _ASSERTE(len != 0);
    _ASSERTE(m_pszText == NULL);

    if (inbuf && (len != 0))
    {
        m_bText = TRUE;

        m_eConvertAction = eConvertAction;
        m_eBomAction     = eBomAction;

        DWORD dwSize = len; // bytes

        // copy buffer to ensure it's null terminated
        BYTE *buf = new BYTE[dwSize + 16];
        memset(buf, 0, dwSize + 16);
        memcpy(buf, inbuf, dwSize);

        BOOL bFoundBom = (buf[0] == 0xFF) && (buf[1] == 0xFE);

        if (m_eConvertAction == ConvertToUnicode)
        {
            int wlen = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)buf, -1, NULL, 0);
#ifndef _UNICODE
            wlen = wlen * sizeof(WCHAR);
#endif
            m_pszText = new wchar_t[wlen + 16];
            memset(m_pszText, 0, (wlen + 16) * sizeof(wchar_t));
            LPWSTR wp = (LPWSTR)m_pszText;
            if ((m_eBomAction == AddBom) && !bFoundBom)
            {
                // caller wants a BOM
                BYTE *p = (BYTE *)m_pszText;
                p[0]    = 0xFF;
                p[1]    = 0xFE;
                wp += 1;
            }
            MultiByteToWideChar(CP_ACP, 0, (LPCSTR)buf, -1, wp, wlen + 2);
            m_nBufLen = wcslen((WCHAR *)m_pszText);
        }
        else if (m_eConvertAction == ConvertToAnsi)
        {
            LPCWSTR wp = (LPCWSTR)buf;
            if (bFoundBom && (m_eBomAction == RemoveBom))
                wp++; // skip over BOM
            int alen  = WideCharToMultiByte(CP_ACP, 0, wp, -1,
                                           NULL, 0, NULL, NULL);
            m_pszText = new wchar_t[alen + 4];
            memset(m_pszText, 0, (alen + 4) * sizeof(wchar_t));
            WideCharToMultiByte(CP_ACP, 0, wp, -1,
                                (LPSTR)m_pszText, alen + 1, NULL, NULL);
            m_nBufLen = strlen((LPCSTR)m_pszText);
        }
        else
        {
            // no conversion
            m_pszText   = new wchar_t[(dwSize + 16) / sizeof(wchar_t)];
            wchar_t *cp = m_pszText;
            memset(m_pszText, 0, dwSize + 8);
            int index = 0;
            if ((m_eBomAction == AddBom) && !bFoundBom)
            {
                BYTE bom[2] = {0xFF, 0xFE};
                memcpy(cp, bom, 2);
                cp += 2;
            }
            else if ((m_eBomAction == RemoveBom) && bFoundBom)
            {
                index = 2;
            }
            memcpy(cp, &buf[index], dwSize);
            m_nBufLen = wcslen(m_pszText);
        }

        m_nPosition          = 0;
        m_bIsOpen            = TRUE;
        m_bDoNotDeleteBuffer = FALSE; // ok to delete the buffer
        delete[] buf;
        rc = TRUE;
    }

    return rc;
}

size_t CResourceTextFile::ReadLine(wchar_t *buf, size_t nBufLen)
{
    size_t nOldPosition = m_nPosition;
    size_t nIndex       = 0;
    if (buf)
        *buf = L'\0';

    if (m_bIsOpen && m_pszText && m_bText)
    {
        while (!IsAtEOF())
        {
            wchar_t c = m_pszText[m_nPosition++];

            if ((c == L'\r') || (c == L'\n'))
            {
                if (!IsAtEOF())
                {
                    // check for \r\n pair
                    wchar_t prevc = c;
                    c             = m_pszText[m_nPosition];
                    if (((prevc == L'\r') && (c == L'\n')) ||
                        ((prevc == L'\n') && (c == L'\r')))
                    {
                        m_nPosition++;
                    }
                }
                break; // end of line
            }

            if (buf && (nIndex < nBufLen))
                buf[nIndex] = c;
            nIndex++;
        }
    }

    // add terminating nul always
    if (buf)
    {
        if (nIndex >= nBufLen)
        {
            // there is not enough room, so replace last char
            nIndex = nBufLen - 1;
            if (nBufLen == 0)
                nIndex = 0;
        }
        buf[nIndex] = L'\0';
    }

    // if we were just getting buffer size, restore position
    if (!buf)
    {
        m_nPosition = nOldPosition;
    }

    return nIndex;
}
